在日常生活中,我們常常需要處理各種待辦事項,無論是讀書計畫、工作安排,還是生活中的小提醒,若能有個工具在適當的時間提醒自己,就能避免遺漏重要的事情。
今天要實作的小程式目標很單純:建立一個簡單的記事提醒工具,讓使用者能輸入事項與提醒時間,並在時間到時自動跳出提示。
那我們馬上開始實作吧!
提醒事項需要能夠「存檔」與「讀檔」,我這裡用 JSON 檔案 reminders.json 來保存,同時也會補上 done 欄位,避免舊資料缺少狀態。
DATA_FILE = "reminders.json"
def load_reminders():
try:
with open(DATA_FILE, "r", encoding="utf-8") as f:
data = json.load(f)
for r in data:
if "done" not in r:
r["done"] = False
return data
except FileNotFoundError:
return []
def save_reminders():
with open(DATA_FILE, "w", encoding="utf-8") as f:
json.dump(reminders, f, ensure_ascii=False, indent=2)
提供使用者新增提醒,並設定日期、時間與重複方式,同時也可以「標記完成」或「刪除」。
def add_reminder():
title = title_entry.get().strip()
date = date_entry.get_date()
hour = hour_var.get()
minute = minute_var.get()
repeat = repeat_var.get()
if not title:
messagebox.showwarning("警告", "請輸入提醒內容!")
return
remind_time = datetime.combine(date, datetime.min.time()).replace(hour=hour, minute=minute)
reminder = {
"title": title,
"time": remind_time.strftime("%Y-%m-%d %H:%M"),
"repeat": repeat,
"done": False
}
reminders.append(reminder)
save_reminders()
refresh_list()
title_entry.delete(0, tk.END)
def toggle_done():
selected = reminder_list.curselection()
if not selected:
return
index = selected[0]
reminders[index]["done"] = not reminders[index]["done"]
save_reminders()
refresh_list()
def delete_reminder():
selected = reminder_list.curselection()
if not selected:
return
index = selected[0]
reminders.pop(index)
save_reminders()
refresh_list()
frame_top = ttk.Frame(root, padding=10)
frame_top.pack(fill="x")
ttk.Label(frame_top, text="提醒內容:").grid(row=0, column=0, padx=5, pady=5)
title_entry = ttk.Entry(frame_top, width=25)
title_entry.grid(row=0, column=1, padx=5, pady=5)
ttk.Label(frame_top, text="日期:").grid(row=1, column=0, padx=5, pady=5)
date_entry = DateEntry(frame_top, width=12, background="darkblue",
foreground="white", date_pattern="yyyy-mm-dd")
date_entry.grid(row=1, column=1, padx=5, pady=5, sticky="w")
hour_var = tk.IntVar(value=12)
minute_var = tk.IntVar(value=0)
hour_spin = ttk.Spinbox(frame_top, from_=0, to=23, textvariable=hour_var, width=5)
minute_spin = ttk.Spinbox(frame_top, from_=0, to=59, textvariable=minute_var, width=5)
frame_mid = ttk.Frame(root, padding=10)
frame_mid.pack(fill="both", expand=True)
reminder_list = tk.Listbox(frame_mid, height=15)
reminder_list.pack(fill="both", expand=True, side="left")
scrollbar = ttk.Scrollbar(frame_mid, orient="vertical", command=reminder_list.yview)
scrollbar.pack(side="right", fill="y")
reminder_list.config(yscrollcommand=scrollbar.set)
frame_bottom = ttk.Frame(root, padding=10)
frame_bottom.pack(fill="x")
done_button = ttk.Button(frame_bottom, text="完成 / 取消完成", command=toggle_done)
done_button.pack(side="left", padx=5)
delete_button = ttk.Button(frame_bottom, text="刪除提醒", command=delete_reminder)
delete_button.pack(side="left", padx=5)
def show_reminder(title):
messagebox.showinfo("記事提醒", title)
這部分是程式的「心臟」,負責定時檢查是否有提醒需要跳出。
def check_reminders():
while True:
now = datetime.now().strftime("%Y-%m-%d %H:%M")
for r in reminders:
if not r["done"] and r["time"] == now:
root.after(0, lambda t=r["title"]: show_reminder(t))
remind_time = datetime.strptime(r["time"], "%Y-%m-%d %H:%M")
if r["repeat"] == "每日":
remind_time += timedelta(days=1)
elif r["repeat"] == "每週":
remind_time += timedelta(weeks=1)
elif r["repeat"] == "每月":
month = (remind_time.month % 12) + 1
year = remind_time.year + (remind_time.month // 12)
day = remind_time.day
try:
remind_time = remind_time.replace(year=year, month=month, day=day)
except ValueError:
next_month = datetime(year, month + 1, 1)
remind_time = next_month - timedelta(days=1)
else:
r["done"] = True
r["time"] = remind_time.strftime("%Y-%m-%d %H:%M")
save_reminders()
refresh_list()
time.sleep(10) # 每 10 秒檢查一次
最後,把所有功能串接起來,啟動 GUI 主迴圈,並在背景執行檢查提醒的執行緒。
reminders = load_reminders()
refresh_list()
threading.Thread(target=check_reminders, daemon=True).start()
root.mainloop()
在這次記事提醒小程式的開發過程中,從最初的簡單文字提醒,到逐步加入日期選擇、重複提醒與完成狀態等功能,每增加一項功能,成就感都會隨之提升。為了讓小程式更貼近生活使用需求,在不斷嘗試與調整語法的過程中,我逐漸理解到軟體開發的核心,其實就是持續迭代與反覆測試。每一次錯誤訊息的排查,都是對耐心與思考方式的磨練。當最終看到一個能獨立運作的小程式誕生時,那份成就感遠遠超過過程中的挫折!
祝大家也在寫這份小程式時能找到屬於這份對於程式碼的熱情~